package com.example.teachingevaluationsystem.config.auth;

import cn.hutool.json.JSONUtil;
import com.example.teachingevaluationsystem.common.ResultCode;
import com.example.teachingevaluationsystem.config.jwt.JwtAuthenticationTokenFilter;
import com.example.teachingevaluationsystem.config.rest.BaseResponse;
import com.example.teachingevaluationsystem.entity.User;
import com.example.teachingevaluationsystem.exception.BusinessException;
import com.example.teachingevaluationsystem.service.IRoleService;
import com.example.teachingevaluationsystem.service.IUserService;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.List;

/**
 * @author macro
 * @date 2018/4/26
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  private final RestfulAccessDeniedHandler restfulAccessDeniedHandler;

  private final IUserService userService;

  private final IRoleService roleService;

  private final PasswordEncoder passwordEncoder;

  @Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
        .csrf() // 由于使用的是JWT，我们这里不需要csrf
        .disable()
        .sessionManagement() // 基于token，所以不需要session
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authorizeRequests()
        .antMatchers( // 允许对于网站静态资源的无授权访问
            "/",
            "/*.html",
            "/favicon.ico",
            "/**/*.html",
            "/**/*.css",
            "/**/*.js",
            "/swagger-ui",
            "/swagger-ui/**",
            "/swagger-resources/**",
            "/v2/api-docs",
            "/v3/api-docs",
            "/webjars/**",
            "/evaluate/evaluate/excel",
            "/download/**")
        .permitAll()
        // 对登录注册要允许匿名访问
        .antMatchers("/login", "/evaluate/user/registered")
        .permitAll()
        .antMatchers(HttpMethod.GET, "/evaluate/role")
        .permitAll()
        // 跨域请求会先进行一次options请求
        .antMatchers(HttpMethod.OPTIONS)
        .permitAll()
        .anyRequest() // 除上面外的所有请求全部需要鉴权认证
        .authenticated();
    // 禁用缓存
    httpSecurity.headers().cacheControl();
    // 添加JWT filter
    httpSecurity.addFilterBefore(
        jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    // 添加自定义未授权和未登录结果返回
    httpSecurity
        .exceptionHandling()
        .accessDeniedHandler(restfulAccessDeniedHandler)
        .authenticationEntryPoint(
            (httpServletRequest, httpServletResponse, e) -> {
              httpServletResponse.setCharacterEncoding("UTF-8");
              httpServletResponse.setContentType("application/json");
              httpServletResponse
                  .getWriter()
                  .println(
                      JSONUtil.parse(
                          BaseResponse.builder()
                              .message("error")
                              .success(false)
                              .code(ResultCode.UN_LOGIN)
                              .build()));
              httpServletResponse.getWriter().flush();
            });
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder);
  }

  @Override
  @Bean
  public UserDetailsService userDetailsService() {
    // 获取登录用户信息
    return username -> {
      // 获取用户名
      User user = userService.loadUserByUserName(username);
      if (user != null) {
        // 获取相关权限
        List<String> roles = roleService.loadUserRoleByUserId(user.getId());
        return org.springframework.security.core.userdetails.User.builder()
            .username(user.getUsername())
            .password(user.getPassword())
            .roles(roles.toArray(new String[] {}))
            .build();
      }
      throw new BusinessException("用户名或密码错误");
    };
  }

  @Bean
  public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
    return new JwtAuthenticationTokenFilter();
  }
}
